package edu.bupt.changelog.history;

import edu.bupt.changelog.history.defaultsaver.Record;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.util.Assert;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class HistoryRegistion {


    private static Map<Class,HistoryRegistInfo> historyInfoMap = new ConcurrentHashMap<>();

    public static Optional<HistoryRegistInfo> tryGet(Class clazz){
        return Optional.ofNullable(historyInfoMap.get(clazz));
    }

    public static void tryPut(Class clazz, HistoryRegistInfo historyRegistInfo){
        historyInfoMap.put(clazz,historyRegistInfo);
    }

    private static final String keySpliter = "@@";
    private static final Integer keyMaxLength = 64;

    public static Optional<String> tryGetEntityKey(Object entity){
        if(entity==null){
            return Optional.empty();
        }

        return tryGet(entity.getClass())
                .map(info -> info.getPrimaryKeys().stream()
                        .map(a -> {
                            try {
                                return PropertyUtils.getProperty(entity, a);
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        })
                        .map(String::valueOf).collect(Collectors.joining(keySpliter))
                );
    }



    public static Record buildRecord(Object oldEntity, Object newEntity){
        // 转map，转map
        // 然后比较
        // 然后输出;

        Record record = new Record();
        if(oldEntity==null && newEntity!=null){
            record.setChangeType(1);
        }else if(oldEntity!=null && newEntity ==null){
            record.setChangeType(3);
        }else if(oldEntity!=null && newEntity != null){
            record.setNewEntityJsonStr(newEntity.toString());
            record.setOldEntityJsonStr(oldEntity.toString());
        }

        return record;

    }

    public static <T> Object get(T userParam, Class clazz) {
        final HistoryRegistInfo info = historyInfoMap.get(clazz);
        Object res = null;
        try {
            res = get(userParam,info.getRepositoryGetMethod(), info.getRepositoryInstance(), info.getPrimaryKeys());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return res;
    }


    public static <UserParam,GetInstance> Object get(UserParam userParam,
                                                             Method getMethod,
                                                             GetInstance getInstance,
                                                             List<String> primaryKeys) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {

        Class<?>[] parameterTypes = getMethod.getParameterTypes();
        final Class firstParamType = parameterTypes[0];
        if(HistoryUtils.isBasicClass(firstParamType)){
            return getByid(userParam,getMethod,getInstance,primaryKeys);
        }else {
            return getByObject(userParam,getMethod,getInstance,primaryKeys);
        }
    }




    public static <UserParam,GetInstance> Object getByObject(UserParam userParam,
                                                     Method getMethod,
                                                     GetInstance getInstance,
                                                     List<String> primaryKeys) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
        // 要校验的

        // 如果get方法的参数是一个对象;【】
        // 如果userparam也是一个对象;
        // 如果userparam是一个基本类型或对象，那么就

        Class<?>[] parameterTypes = getMethod.getParameterTypes();
        Class p = parameterTypes[0];
        Object param = p.getConstructor().newInstance();


        if(HistoryUtils.isPrimitiveOrString(userParam)){
            PropertyUtils.setProperty(param,primaryKeys.get(0),userParam);
        }else {
            for (String key : primaryKeys) {
                Object property = PropertyUtils.getProperty(userParam, key);
                PropertyUtils.setProperty(param, key, property);
            }
        }

        Object invoke = getMethod.invoke(getInstance, param);
        return invoke;
    }

    // 如果get方法，只是支持1个基本类型orString，那么走这里;【check primaryKeys一定只有一个】
    // 如果userparam也是一个基本类型或string;
    // 如果userparam是一个对象;
    public static <UserParam,GetInstance> Object getByid(UserParam userParam,
                                                                              Method getMethod,
                                                                              GetInstance getInstance,
                                                                              List<String> primaryKeys) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Object param = null;
        if(HistoryUtils.isPrimitiveOrString(userParam)){
            param = userParam;
        }else {
            //String property = BeanUtils.getProperty(userParam, primaryKeys.get(0));
            param = PropertyUtils.getProperty(userParam, primaryKeys.get(0));
            Assert.notNull(param,"请求中未包含primaryKey信息");
        }

        Object invoke = getMethod.invoke(getInstance, param);

        return invoke;
    }


    public static class HistoryRegistInfo{
        private Object repositoryInstance;
        private Method repositoryGetMethod;
        private List<String> primaryKeys=new ArrayList<>();

        public Object getRepositoryInstance() {
            return repositoryInstance;
        }

        public void setRepositoryInstance(Object repositoryInstance) {
            this.repositoryInstance = repositoryInstance;
        }

        public Method getRepositoryGetMethod() {
            return repositoryGetMethod;
        }

        public void setRepositoryGetMethod(Method repositoryGetMethod) {
            this.repositoryGetMethod = repositoryGetMethod;
        }

        public List<String> getPrimaryKeys() {
            return primaryKeys;
        }

        public void setPrimaryKeys(List<String> primaryKeys) {
            this.primaryKeys = primaryKeys;
        }
    }


}
